package net.sf.rose.jdbc.dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.List;

import org.apache.log4j.Logger;

import net.sf.rose.jdbc.query.QuerySQL;
import net.sf.rose.jdbc.service.Service;
import net.sf.rose.util.ClassUtils;
import net.sf.rose.util.RoseRuntimeException;

/**
 * 类说明：通用数据库操作的封装类
 * 
 * @author fengjian
 * @since 2015/05/22
 */
public abstract class GenericDAO {

	private static Logger LOG = Logger.getLogger(GenericDAO.class);

	/** 无序的数据(HashMap) */
	public static int RETURN_DATA_TYPE_HASHMAP = 0;
	/** 有序的数据(LinkedHashMap) */
	public static int RETURN_DATA_TYPE_LINKEDHASHMAP = 1;

	/** 数据库服务类 */
	protected Service service = null;

	protected Connection con = null;

	protected PreparedStatement pstmt = null;

	protected ResultSet rs = null;

	protected String logString = null;

	protected QuerySQL query = null;

	/** 查询返回数据类型 */
	protected int returnDataType = 0;

	/** 批量更新影响的记录数 */
	private int batchUpdateRecordCount = 0;

	/**
	 * 设置数据库服务类
	 * 
	 * @param service - 数据库服务类
	 */
	public void setService(Service service) {
		this.service = service;
	}

	/**
	 * 设置数据库服务类
	 * 
	 * @return 数据库服务类
	 */
	public Service getService() {
		return this.service;
	}

	/**
	 * 返回数据库操作SQL语句生成实例
	 * 
	 * @param <T> 实体类对象类型
	 * @return 返回取得的数据类
	 */
	public abstract <T extends QuerySQL> T getQuerySQL();

	/**
	 * 功能:从数据库中读取指定的数据类
	 * <p>
	 * 如果查询没有结果，则抛出异常
	 * <p>
	 * 如果查询有多个结果，则抛出异常
	 * 
	 * @param <T> 返回的数据库表映射实体类类型
	 * @return 返回取得的数据类
	 */
	public abstract <T> T load();

	/**
	 * 功能:从数据库中读取指定的数据类
	 * <p>
	 * 如果查询没有结果，则返回null
	 * <p>
	 * 如果查询有多个结果，则返回第一条记录
	 * 
	 * @param <T> 返回的数据库表映射实体类类型
	 * @return 返回取得的数据类
	 */
	public abstract <T> T get();

	/**
	 * 功能:从数据库中读取数据类列表
	 * 
	 * @param <T> 返回的数据库表映射实体类类型
	 * @return 返回取得的结果数据集
	 */
	public abstract <T> List<T> list();

	/**
	 * 功能:执行数据库更新
	 * 
	 * @return 返回数据库更新影响的行数
	 */
	public int update() {
		// 执行SQL语句
		try {
			// 拼写SQL语句日志
			logString = query.getLog().trim();
			this.con = service.getConnection();
			this.fillStatement(query.getSql(), query.getParameterList(), true);
			int i = pstmt.executeUpdate();
			saveLog(logString, i);
			// 设置数据库连接关闭前必须提交
			service.setNeedCommit(true);
			return i;
		} catch (SQLException e) {
			throw ClassUtils.getException("数据库update更新操作失败！sql:" + getLogString(logString), e);
		} catch (Exception e) {
			throw ClassUtils.getException("数据库update更新发生未知异常！", e);
		} finally {
			closeResult();
		}
	}

	/**
	 * 功能:执行数据库操作
	 * 
	 * @return 返回数据库更新操作是否成功
	 */
	public boolean execute() {
		// 执行SQL语句
		try {
			// 拼写SQL语句日志
			logString = query.getLog().trim();
			this.con = service.getConnection();
			this.fillStatement(query.getSql(), query.getParameterList(), true);
			int i = pstmt.executeUpdate();
			saveLog(logString, i);
			// this.con.commit();
			// 设置数据库连接关闭前必须提交
			service.setNeedCommit(true);
			return i == 1;
		} catch (SQLException e) {
			throw ClassUtils.getException("数据库execute更新操作失败！sql:" + getLogString(logString), e);
		} catch (Exception e) {
			throw ClassUtils.getException("数据库execute更新发生未知异常！", e);
		} finally {
			closeResult();
		}
	}

	/**
	 * 功能:执行数据库批量操作
	 * 
	 * @return 操作结果值
	 */
	public int[] batchUpdate() {
		Object[] arr;
		try {
			// 拼写SQL语句日志
			logString = query.getLog().trim();
			this.con = service.getConnection();
			pstmt = con.prepareStatement(query.getSql());
			List<Object> list = query.getParameterList();
			for (Object val : list) {
				// 如果是数组
				if (val instanceof Object[]) {
					arr = (Object[]) val;
					for (int i = 0; i < arr.length; i++) {
						if (arr[i] instanceof java.util.Date) {
							arr[i] = new java.sql.Timestamp(((java.util.Date) arr[i]).getTime());
						}
						pstmt.setObject(i + 1, arr[i]);
					}
				}
				// 如果是单个参数
				else {
					if (val instanceof java.util.Date) {
						val = new java.sql.Timestamp(((java.util.Date) val).getTime());
					}
					pstmt.setObject(1, val);
				}
				pstmt.addBatch();
			}
			int[] i = pstmt.executeBatch();
			pstmt.close();
			batchUpdateRecordCount = 0;
			for (int k : i) {
				batchUpdateRecordCount = batchUpdateRecordCount + k;
			}
			saveLog(logString, batchUpdateRecordCount);
			// 设置数据库连接关闭前必须提交
			service.setNeedCommit(true);
			return i;
		} catch (SQLException e) {
			throw ClassUtils.getException("数据库batchUpdate批处理操作失败！sql:" + getLogString(logString), e);
		} catch (Exception e) {
			throw ClassUtils.getException("数据库batchUpdate批处理发生未知异常！", e);
		} finally {
			closeResult();
		}
	}

	/**
	 * 设置查询返回数据类型
	 * 
	 * @param returnDataType - 返回数据类型
	 */
	public void setReturnDataType(int returnDataType) {
		this.returnDataType = returnDataType;
	}

	/**
	 * @return 获取批量更新影响的记录数
	 */
	public int getBatchUpdateRecordCount() {
		return batchUpdateRecordCount;
	}

	/**
	 * 功能:将参数载入PrepareStatement类中
	 * 
	 * @param sql  SQL语句
	 * @param list 参数容器类
	 * @throws SQLException 数据库操作异常
	 */
	protected void fillStatement(String sql, List<Object> list) throws SQLException {
		this.fillStatement(sql, list, false);
	}

	/**
	 * 功能:将参数载入PrepareStatement类中
	 * 
	 * @param sql        SQL语句
	 * @param list       参数容器类
	 * @param updateAble 是否允许更新
	 * @throws SQLException 数据库操作异常
	 */
	protected void fillStatement(String sql, List<Object> list, boolean updateAble) throws SQLException {
		// 如果是更新语句，不允许更新全表。特殊情况请使用where 1=1处理
		if (sql.indexOf("update ") != -1 || sql.indexOf("UPDATE ") != -1) {
			// 如果没有条件
			if (sql.indexOf("where ") == -1 && sql.indexOf("WHERE ") == -1) {
				String message = "不允许更新整个表的数据，更新SQL语句没有任何条件！";
				throw new RoseRuntimeException(message + "sql=" + sql);
			}
		}
		int update = updateAble ? ResultSet.CONCUR_UPDATABLE : ResultSet.CONCUR_READ_ONLY;
		// pstmt = con.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, update);
		pstmt = con.prepareStatement(sql, ResultSet.TYPE_SCROLL_SENSITIVE, update);
		if (list != null && list.size() > 0) {
			int index = 1;
			for (Object o : list) {
				if (o instanceof java.util.Date) {
					o = new java.sql.Timestamp(((java.util.Date) o).getTime());
				}
				pstmt.setObject(index, o);
				index++;
			}
		}
	}

	/**
	 * 功能:关闭查询结果集
	 * <p>
	 * 包括PreparedStatement和ResultSet二个对象的关闭操作
	 * <p>
	 * 此方法执行时若发生异常将不被抛出。
	 */
	protected void closeResult() {
		try {
			if (rs != null) {
				rs.close();
				rs = null;
			}
			if (pstmt != null) {
				pstmt.close();
				pstmt = null;
			}
		} catch (SQLException e) {
			rs = null;
			pstmt = null;
		}
	}

	/**
	 * 功能:返回结果集的元数据名数组。
	 * 
	 * @param rs - ResultSet结果集
	 * @return String[]列名称数据
	 * @throws SQLException - 如果操作失败将抛出异常
	 */
	protected String[] getColNames(ResultSet rs) throws SQLException {
		ResultSetMetaData rsmd = rs.getMetaData();
		int columnCount = rsmd.getColumnCount();
		String[] colnames = new String[columnCount];
		for (int i = 1; i <= columnCount; i++) {
			colnames[i - 1] = rsmd.getColumnLabel(i).toUpperCase();
		}
		return colnames;
	}

	/**
	 * 输入日志
	 * 
	 * @param sql - SQL文日志
	 * @return 加入操作人用户名后的日志
	 */
	protected String getLogString(String sql) {
		String str = "current thread:" + Thread.currentThread().getId() + ";sql=" + sql;
		Object user = service.getUser();
		return user != null ? "username=" + user.toString() + ';' + str : str;
	}

	/**
	 * 功能：记录日志
	 * 
	 * @param logString - 日志内容
	 * @param i         - 数据库操作影响的记录行数
	 */
	protected void saveLog(String logString, int i) {
		if (this.isRecordLog()) {
			LOG.warn(getLogString(logString) + "\n此次数据库执行影响的行数:" + i);
		}
	}

	public boolean isRecordLog() {
		return this.service.isRecordLog();
	}

}
